#include "Engine/Engine.h"

#include "Graphics/OpenGL/GLRenderEngine.h"
#include "OS/WindowEventUtils.h"
#include "Engine/FrameEvent.h"

#if TYRO_DEFINITION == TYRO_DEFINITION_WIN32
#include "Framework/Win32/Win32Timer.h"
#elif TYRO_DEFINITION == TYRO_DEFINITION_ANDROID
#include "Framework/Android/AndroidTimer.h"
#endif

namespace tyro
{
	namespace Engine
	{
		Engine::Engine(  ) 
		:	renderEngine(0)
		,	running(false)
		,	timer(0)
		{


#if TYRO_DEFINITION == TYRO_DEFINITION_WIN32
			timer = new Framework::Win32Timer();
#elif TYRO_DEFINITION == TYRO_DEFINITION_ANDROID
			timer = new Framework::AndroidTimer();
#endif

			renderEngine = new Graphics::GLRenderEngine(this);
		}

		Engine::~Engine(  ) 
		{
			delete renderEngine;
			delete timer;
		}


		Graphics::IRenderWindow* Engine::Initialise( const String& windowTitle )
		{
			timer->Reset();

			return renderEngine->Initialise();
		}

		void Engine::BeginRendering( void )
		{

			// Clear the current frame times
			ClearFrameEventTimes();

			running = true;
			while(running)
			{

#if TYRO_DEFINITION != TYRO_DEFINITION_ANDROID
				// Handle OS messaging
				OS::WindowEventUtils::MessageHandler();
#endif

				// Render the frame
				if (!RenderFrame())
				{
					break;
				}

			}

		}

		bool Engine::RenderFrame(void)
		{
			// Start the engine frame
			if (!StartFrame())
			{
				return false;
			}

			// Update all render targets
			if (!UpdateRenderTargets())
			{
				return false;
			}
		
			// End the engine frame
			return EndFrame();

		}

		bool Engine::UpdateRenderTargets( void )
		{
			renderEngine->UpdateRenderTarget();

			// Queue the engine frame
			bool ret = QueueFrame();

			// Swap the render targets buffers
			renderEngine->SwapRenderTargetBuffers();

			return ret;
		}

#pragma region FrameEventSystem

		bool Engine::StartFrame()
		{
			FrameEvent evt;

			InitialiseFrameEvent(FRAMEEVENT_STARTED, evt);

			return StartFrame(evt);
		}

		bool Engine::StartFrame( FrameEvent& evt )
		{
			// Alert all frame listeners the frame has started
			set<FrameListener*>::type::iterator it;
			for (it= frameListeners.begin(); it != frameListeners.end(); ++it)
			{
				if (!(*it)->FrameStarted(evt))
					return false;
			}

			return true;
		}

		bool Engine::QueueFrame( void )
		{
			FrameEvent evt;
			InitialiseFrameEvent(FRAMEEVENT_QUEUED, evt);
			return QueueFrame(evt);
		}

		bool Engine::QueueFrame( FrameEvent& evt )
		{
			// Alert all frame listeners the frame has been queued
			set<FrameListener*>::type::iterator it;
			for (it= frameListeners.begin(); it != frameListeners.end(); ++it)
			{
				if (!(*it)->FrameQueued(evt))
					return false;
			}

			return true;
		}

		bool Engine::EndFrame()
		{
			FrameEvent evt;
			InitialiseFrameEvent(FRAMEEVENT_ENDED, evt);
			return EndFrame(evt);
		}

		bool Engine::EndFrame( FrameEvent& evt )
		{
			// Alert all frame listeners the frame has ended
			set<FrameListener*>::type::iterator it;
			for (it= frameListeners.begin(); it != frameListeners.end(); ++it)
			{
				if (!(*it)->FrameEnded(evt))
					return false;
			}

			return true;
		}

		void Engine::ClearFrameEventTimes( void )
		{
			for(int i=0; i<FRAMEEVENT_COUNT; ++i)
				frameEventTimes[i].clear();
		}

		void Engine::InitialiseFrameEvent( FrameEventTypes frameEventType, FrameEvent& evt )
		{
			ULONG_32 now = timer->GetMilliseconds();
			evt.lastEventTime = CalculateFrameTime(now, FRAMEEVENT_ANY);
			evt.lastFrameTime = CalculateFrameTime(now, frameEventType);
		}

		FLOAT_32 Engine::CalculateFrameTime( ULONG_32 currentTime, FrameEventTypes frameEventType )
		{
			FrameEventTimesQueue& frameTimes = frameEventTimes[frameEventType];
			frameTimes.push_back(currentTime);

			if (frameTimes.size() == 1)
			{
				return 0; 
			}

			// Times up to mFrameSmoothingTime seconds old should be kept
			unsigned long discardThreshold =
				static_cast<unsigned long>(0.0f * 1000.0f);

			// Find the oldest time to keep
			FrameEventTimesQueue::iterator it = frameTimes.begin(),
				end = frameTimes.end()-2; // We need at least two times
			while(it != end)
			{
				if (currentTime - *it > discardThreshold)
					++it;
				else
					break;
			}

			// Remove old times
			frameTimes.erase(frameTimes.begin(), it);

			return FLOAT_32 ( frameTimes.back() - frameTimes.front() ) / ((frameTimes.size()-1 * 1000));
		
		}

		void Engine::AddFrameListener( FrameListener* frameListener )
		{
			frameListeners.insert(frameListener);
		}

#pragma endregion FrameEventSystem


#pragma region SceneGraph

		Scene::SceneGraph* Engine::GetSceneGraph() const
		{
			// Return the current scene graph
			return sceneGraph;
		}

		Scene::SceneGraph* Engine::CreateSceneGraph()
		{
			// Create the scene graph
			sceneGraph = new Scene::SceneGraph(this);

			return sceneGraph;
		}

#pragma endregion SceneGraph

#pragma region RenderEngine

		Graphics::IRenderEngine* Engine::GetRenderEngine( void )
		{
			return renderEngine;
		}

#pragma endregion RenderEngine


	}
}
